This is my first alpha release of the TimPlayer module.

TimPlayer is a soundtrack player that currently supports the following formats:

- standard MODs
- PC MODs (with more than 4 channels, already found a 28 channels one)
- S3M (ADPCM samples and adlib samples not supported)
- ITs (a few) with no instrument mode, no packed samples (v 214 and above)

planned (top priority):
- instrument mode and envelopes, which means more ITs and XM.
- saving songs as WAVs, usefull for slow machines.
- support for 16-bit hardware (SWIs description and tester required).


!DigitalCD has been setup to use TimPlayer for the relevant file types, so start
DigitalCD and drop your files on it's music files control pannel. Should you
experience problems then remove every 'tim' entry in the file !DigitalCD.Setup
and !DigitalCD will use the good old trusted modules.

While playing you can run the View program which provides a little visualisation
of the music (N.B.: it's for RISC OS >= 3.5 only).

The main features are turned toward sound quality rather than speed:
- internal usage of 16-bit linear buffers mapped after mixing in 8-bit log
  buffers using all the significant bits: 12 instead of the usual 8-bit table
  (quiet sounds are much clearer).
- no multiplications approximated by shifts
- sample interpolation
- mixing of up to 64 channels
- patterns with up to 256 rows
- support for multiple effects on the same channel
- timing of song position and length
- Precalculated Gain Control (an attempt to boost the sound of to quiet songs)


Before you start using TimPlayer a few words of warning first:
- all I know is that it works on my SA RPC with 8-bit sound hardware (reports
  for other setups are very welcome).
- loading of songs can be quite slow due to byte by byte reading of patterns
  info, timing and PGC precalculations (which basically scan the whole patterns
  as if playing to determine elapsed time and max sound amplitude).
- those with less faster machine should be warned that the CPU usage can be
  quite high: at 24us, a little above 1% of CPU per channel on my SA RPC
              at 72us, Cache off, 4 channels grabs 80% of CPU
  As a result playing songs with many channels on slow machines will result in
  grabbing 100% of the CPU, you will never have the control back and be forced
  to reset the machine (any code for controlling CPU usage is welcome).
- it's my first attempt at writing a module and using sound DMA.


I have noticed that altering the stereo position of hardware
channels tends to produce noises (which often leads to
a small bang when at song startup I switch from 1 to 8 channels
and reconfigure the stereo positions). As anyone else
experienced the same problems and/or found a solution to it?


You can contact me using email at:

tim@swn.sni.be (which I can only access between Monday and Thursday).

or using smail address:
Timmermans Andr
Rue du Vert Coron, 17
7600 Pruwelz
Belgium

--------------------------------------------------------------------------------

SWI Interface
=============

Ok, it's alpha, so SWIs are subject to change and descriptions
are minimalistic.

TimPlayer_Load &51380
---------------------

In  - R0 filename pointer

Out -

TimPlayer_Play &51381
---------------------

In  -

Out -

TimPlayer_Pause &51382
----------------------

In  -

Out -

TimPlayer_Stop &51383
---------------------

In  -

Out -

Removes also the song from memory.

TimPlayer_Position &51384
-------------------------

In  - R0 new Sequence Position, -1 to read

Out - R0 Sequence Position
      R1 Current Row in Pattern
      R2 Current Pattern
      R3 Current Pattern Length
      R4 Time Index (in ms)

TimPlayer_SongInfo &51385
-------------------------

In  -
Out - R0 song name ptr
      R1 mod type ptr
      R2 number of channels
      R3 seq len
      R4 min pitch
      R5 max pitch
      R6 16-bit buffers len
      R7 Time Length (in ms)

TimPlayer_ChannelParams &51386
------------------------------

In  - R0 channel nr

Out - R1 16-bit buffer ptr
      R2 instrument nr
      R3 pitch
      R4 buffer peak value [0,&7FFF]
      R5 buffer mean value [0,&7FFF]
      R6 panning [-127 (left), 127 (right)]

TimPlayer_Volume &51387
-----------------------

In  - R0 new volume [0,256], -1 to read

Out - R0 current volume

TimPlayer_Configure &51388
--------------------------

In  - R0 code
    - R1 value, -1 to read

Out - R1 current value

R0 code:
 0  - R1 = hardware speed [24-72 us]

TimPlayer_SongParams &51389
---------------------------

Out - R0 current speed
    - R1 current tempo
    - R2 current global volume

--------------------------------------------------------------------------------

Timing explained
================

Due to the nature of soundtracks, timing is not a straightforward issue.
So first let me explain briefly how a soundtrack is played:

1. A song is composed of a sequence of patterns which are played in turn.
   Example the sequence 1-3-5-1-1-2 means play pattern nr 1, then pattern
   nr 3 then nr 5, then nr 1 again, etc.

2. Each pattern is divided in n rows (64 in general) which are played in turn.
   A row contains information to apply to each defined channel: instrument,
   note to play, volume, panning, commands (effects) to apply. When a row is
   played, first the row is decoded, then commands are applyed and the note
   is maintained for a given duration called a frame (or sometimes a tick).
   Then commands are re-applyed and the note is maintained for the same
   duration. Then ...

   The duration of a row is determined by two parameters:
   - the Tempo or number of beats per minutes (125 by default)
     and the relation: nr of frames/sec = 0.4 * tempo.
   - the Speed or number of frames per row (6 by default).

So, the duration of a song could simply be:

sum for each pattern in the sequence of "nr of rows * speed * .4 * tempo".

The problem is the existence of special commands that interfers with this,
such as:

- Set Tempo, to alter the tempo
- Set Speed, to alter the speed
- Pattern break, to stop playing the current pattern and to go
  in row x of the next pattern in the sequence
- Sequence Jump, to stop playing current pattern and to go
  in row 0 of pattern at sequence index x.
- Loop, to loop x times on a set of rows.

The best way I have found is to perform the song without actually playing
but applying only the above commands, adding together the resulting
frame durations and storing this time index at the start of each position
in the pattern sequence until I reach a the end of a sequence or I branch
to a sequence position I have already timed.

As this may not cover all the positions in the sequence (for example all
the songs in the game Lemmings are stored in the same soundtrack and are
played by moving to different looping sequence fragments) I then move
to the first untimed sequence position and continue the timing from that
point. And again and again until every sequence position is timed.
The final timing obtained is the time length reported by TimPlayer_SongInfo.

During real playback I maintain a timer that can be obtained on return of
TimPlayer_Position. The timer is reset at every sequence position change
to the time index store for that position, then time is added normally
while the pattern is played.

Those of you with some knowledge of how soundtracks works will noticed
immediately the remaining problems:

- by combining Pattern Break and Sequence Jumps it is possible
  to perform 2 or more sets of differents rows of the same pattern
  position (and I have only timed one of them) or to perform
  the same set with different tempo/speed settings.

- when the user decide to skip a part of the somg he may bypass
  some Set Tempo/Set Speed, which means that the song will then
  continue to play with abnormal tempo/speed settings.
